﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Agent : MonoBehaviour
{
    private Vector3 acceleration;
    private float nextRandomTime;

    private float nextRandomSpeedTime;
    private float currentSpeedCoefficient = 1;

    private float minSpeedCoefficient = 0.6f;
    private float maxSpeedCoefficient = 1.5f;

    private Manager manager;
    List<Agent> neighbors;

    private void Start()
    {
        manager = Manager.Instance;
    }

    private void Update()
    {
        neighbors = manager.GetAgentsInView(this, manager.viewDistance);

        Vector3 targetForce = GetTargetForce();
        Vector3 aimAcceleration = GetCohesionForce() + GetDispersionForce() + GetQueueForce() + GetTargetForce() + targetForce;

        if (Time.time >= nextRandomTime)
        {
            nextRandomTime = Time.time + 1f;

            aimAcceleration += GetRandomForce();
        }

        acceleration = Vector3.Lerp(acceleration, aimAcceleration, 50 * Time.deltaTime);

        Vector3 aimDir = transform.forward * manager.speed + acceleration * Time.deltaTime;

        transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(aimDir), 50 * Time.deltaTime);

        if (Time.time >= nextRandomSpeedTime)
        {
            nextRandomSpeedTime = Time.time + 0.5f;

            currentSpeedCoefficient = currentSpeedCoefficient * Random.Range(0.8f, 1.2f);
        }

        if(currentSpeedCoefficient < minSpeedCoefficient)
            currentSpeedCoefficient = minSpeedCoefficient;

        if (currentSpeedCoefficient > maxSpeedCoefficient)
            currentSpeedCoefficient = maxSpeedCoefficient;

        transform.position = transform.position + transform.forward * manager.speed * Time.deltaTime * currentSpeedCoefficient;
    }

    private Vector3 GetCohesionForce()
    {
        Vector3 force = Vector3.zero;
        
        if (neighbors.Count == 0) return force;

        Vector3 center = Vector3.zero;
        
        foreach(var item in neighbors)
        {
            center += item.transform.position;
        }
        center /= neighbors.Count;

        force = (center - transform.position) * manager.cohesionCoefficient;

        return force;
    }

    private Vector3 GetDispersionForce()
    {
        Vector3 force = Vector3.zero;

        if (neighbors.Count == 0) return force;

        foreach (var item in neighbors)
        {
            Vector3 reverseForce = transform.position - item.transform.position;
            force += reverseForce.normalized * (1.0f / reverseForce.magnitude) * manager.dispersionCoefficient;
        }

        return force;
    }

    private Vector3 GetQueueForce()
    {
        Vector3 force = Vector3.zero;

        if (neighbors.Count == 0) return force;

        foreach (var item in neighbors)
        {
            force += item.transform.forward * manager.queueCoefficient;
        }
        return force;
    }

    private Vector3 GetRandomForce()
    {
        Vector3 force = Vector3.zero;
        force.x = Random.Range(-1f, 1f);
        force.y = Random.Range(-1f, 1f);
        force.z = Random.Range(-1f, 1f);

        force *= manager.randomCoefficient;

        return force;
    }

    private Vector3 GetTargetForce()
    {
        Vector3 force = Vector3.zero;

        if (manager.target != null && Vector3.Distance(manager.target.transform.position, transform.position) > 1.5f)
            force = manager.target.transform.position - transform.position;

        force = force.normalized * manager.targetCoefficient;

        return force;
    }
}
